
#include <stddef.h>
#include <string.h>
#include <Utility.h>
#include <phApp_Init.h>
#include <phNfcLib_Int.h>
#include <phNfcLib_Initialization.h>
#include "BSP.h"
#include "Reader.h"
#include "log/log2.h"

#include "config/config2.h"

#include <Reader/Reader.h>
#include <Reader/ReaderDesfire2.h>

typedef struct _mf2go_param{
	uint8_t keys[16];
	uint8_t df_name[16];
	uint8_t aid[3];
	uint8_t file_no;
	uint8_t key_no;
	uint8_t key_type;
}w_mf2go_param_t;

typedef struct {
	uint8_t format;
	uint8_t terminal_info_mode;
	uint8_t type;
	uint8_t sub_type;
	uint8_t tci[3];
}ecp_param_t;

typedef struct {
	uint32_t aid;
	uint8_t keys[16];
	uint8_t key_type;
	uint8_t key_no;
	uint8_t diversified;
}key_set_t;

typedef struct _apple_wallet{
	key_set_t key_set[2];
	uint8_t file_no;
	uint8_t file_size;
}w_apple_wallet_param_t;

w_mf2go_param_t mf2go_param = {
	.keys = {0xD4,0x28,0x31,0xB1,0x16,0x43,0x68,0x44,0xD5,0xAE,0xD5,0x67,0xB1,0xDA,0x14,0xE2},
	.df_name = {0xa0, 0x00, 0x00, 0x03, 0x96, 0x56, 0x43, 0x41, 0x03, 0xf5, 0x32, 0xf0, 0x00, 0x02, 0x09, 0x01},
	.aid = {0xF5,0x32,0xF0},
	.file_no = 0x02,
	.key_no = 0x01,
	.key_type = PH_KEYSTORE_KEY_TYPE_AES128,
};

w_apple_wallet_param_t apple_wallet_param = {
	.key_set = {
		[0] = {
			.aid = 0xA11E00,
			.keys = {0x30, 0x84, 0x5e, 0xf6, 0x5b, 0x17, 0x25, 0x20, 
					0xb7, 0x57, 0x36, 0x01, 0x2c, 0x24, 0xd1, 0x16},
			.key_type = PH_KEYSTORE_KEY_TYPE_2K3DES,
			.key_no = 0x00,
			.diversified = 0,
		},
		[1] = {
			.aid = 0xA11E01,
			.keys = {0xab, 0x43, 0x7c, 0xb9, 0xf5, 0x9c, 0xb8, 0x19, 
					0xf6, 0x70, 0x9d, 0xc1, 0x8f, 0xea, 0xf0, 0xb4},
			.key_type = PH_KEYSTORE_KEY_TYPE_AES128,
			.key_no = 0x00,
			.diversified = 1,
		},
	},
	.file_no = 0x01,
	.file_size = 16,
};


/*===================================================================================*/
#pragma pack(1)
	typedef struct {
		uint8_t tag1;
		uint8_t length1;
		uint8_t tag2;
		uint8_t length2;
		uint8_t info;
		uint8_t pdcap[2];
		uint8_t vcuid[7];
		uint8_t version_id;
		uint8_t timestamp[5];
	}fci_info_t;

	typedef struct {
		uint8_t major;
		uint8_t minor;
		uint8_t customer_code[5];
		uint8_t credential_id[8];
		uint8_t reissue_code;
		uint8_t pin_code[4];
		uint8_t customer_data[20];
		uint8_t signature[8];
	}acd_ev1_t;

	typedef struct {
		uint16_t head;
		uint8_t data[8];
		uint8_t wg_num;
		uint8_t wg_format;
	}default_data_t;
	
	typedef struct {
		uint8_t wg_num;
		uint8_t wg_format;
		uint8_t data[18];
	}custom_data_t;
#pragma pack()



static void reader_get_access_data(uint8_t *customer_data, credential_t* cred){
	default_data_t* def_data = (default_data_t*)customer_data;
	if(def_data->head == 0x1001){
		//默认格式
		uint8_t len = def_data->wg_num / 8;
		if(def_data->wg_num & 0x07){
			len++;
		}
		cred->nbit = def_data->wg_num;
		cred->clen = len;
		Mem_Backward_Copy(cred->credential, len, def_data->data, sizeof(def_data->data), len);
	}
	else{
		//自定义格式
		custom_data_t* custom = (custom_data_t*)customer_data;
		uint8_t len = custom->wg_num / 8;
		if(custom->wg_num & 0x07){
			len++;
		}
		cred->nbit = custom->wg_num;
		cred->clen = len;
		Mem_Backward_Copy(cred->credential, len, custom->data, sizeof(custom->data), len);
	}
	cred->tag.type = HFTAG_WAVELYNX;
}

static void reader_print_fci(const fci_info_t* fci){
	LOG2_PRINT("\n===========FCI============\n");
	LOG2_PRINT("info: %02X\n", fci->info);
	LOG2_PRINT("pdcap: %02X%02X\n", fci->pdcap[0], fci->pdcap[1]);
	LOG2_PRINT("vcuid: %02X%02X%02X%02X%02X%02X%02X\n", fci->vcuid[0], fci->vcuid[1], 
		fci->vcuid[2], fci->vcuid[3], fci->vcuid[4], fci->vcuid[5], fci->vcuid[6]);
	LOG2_PRINT("version_id: %02X\n", fci->version_id);
	LOG2_PRINT("timestamp: %02X%02X%02X%02X%02X\n", fci->timestamp[0], fci->timestamp[1], 
		fci->timestamp[2], fci->timestamp[3], fci->timestamp[4]);
	LOG2_PRINT("===========================\n");
}

static uint8_t reader_mf2go_gen_div(const fci_info_t* fci, const uint8_t aid[3], uint8_t *div){
	uint8_t len = 0;
//		div[len++] = 0x01;
	memcpy(&div[len], fci->vcuid, sizeof(fci->vcuid));
	len += sizeof(fci->vcuid);
	memcpy(&div[len], aid, 3);
	len += 3;
	div[len++] = fci->info;
	memcpy(&div[len], fci->timestamp, sizeof(fci->timestamp));
	len += sizeof(fci->timestamp);
	return len;
}

static uint32_t reader_read_google_wallet(w_mf2go_param_t* param, credential_t* cred){
	int ret;
	phStatus_t status;
	void* pDataParams  = phNfcLib_GetDataParams(PH_COMP_AL_MFDFEVX);
	uint8_t keyNo = 0;
	uint8_t ver = 0;
	void* key_store = phNfcLib_GetDataParams(PH_COMP_KEYSTORE);
	//1. Activate and identify credential using ISO SELECT.
	fci_info_t fci_info = {0};
{
	uint8_t *pFCI = NULL;
	uint16_t wFCILen = 0;
	status = phalMfdfEVx_IsoSelectFile(pDataParams, 0x00U, 0x04, NULL, 
		(uint8_t*)param->df_name, sizeof(param->df_name), 0, &pFCI, &wFCILen);
	if(status){
		LOG2_ERR("phalMfdfEVx_IsoSelectFile fail: %04X", status);
		return 1;
	}
	LOG2_BUF_DBG("FCI", pFCI, wFCILen);
	
	uint8_t len = wFCILen < sizeof(fci_info) ? wFCILen : sizeof(fci_info);
	memcpy(&fci_info, pFCI, len);
	reader_print_fci(&fci_info);
}

//		2. DESFire application 0xF532F0 is auto selected as primary application.
//		3. Read/Write encryption key for application is diversified using FCI data.
	uint8_t div[32] = {0};
	uint8_t dlen = reader_mf2go_gen_div(&fci_info, param->aid, div);
	
	LOG2_BUF_DBG("div", div, dlen);

//		4. DESFire application is authenticated using key 1.
{
		keyNo = 1;
		LOG2_BUF_DBG("key", param->keys, sizeof(param->keys));
		phKeyStore_FormatKeyEntry(key_store, keyNo, param->key_type);
		phKeyStore_SetKeyAtPos(key_store, keyNo, 0, param->key_type, param->keys, ver);
		ret = Reader_MFDF_AuthenticateAES(PHAL_MFDFEVX_DIV_METHOD_CMAC, keyNo, ver, param->key_no, div, dlen);
		if(ret){
			LOG2_ERR("Reader_MFDF_Authenticate fail: %04X", ret);
			return 2;
		}
}

//		5. Credential data is read from file 2 of application.
//	acd_ev1_t acd = {0};
	uint8_t bFileNo = 2;

{
	uint8_t *data;
	uint16_t dlen = 0;
	uint8_t offset[3] = {0x00, 0x00, 0x00};
	uint8_t length[3] = {sizeof(acd_ev1_t), 0x00, 0x00};
	LOG2_DBG("read data");
	ret = Reader_MFDF_ReadData(PHAL_MFDFEVX_COMMUNICATION_ENC, PHAL_MFDFEVX_APPLICATION_CHAINING, bFileNo, offset, length, &data, &dlen);
	if(ret){
		LOG2_ERR("Reader_MFDF_ReadData fail: %04X", ret);
		return 3;
	}
	LOG2_BUF_DBG("data", data, dlen);
	if(dlen != sizeof(acd_ev1_t)){
		LOG2_ERR("dlen=%d, len=%d", dlen, sizeof(acd_ev1_t));
		return 4;
	}
//		SimpleTag_T tag;
	acd_ev1_t* acd = (acd_ev1_t*)data;
	reader_get_access_data(acd->customer_data, cred);
	LOG2_DBG("wg: %d", cred->nbit);
	LOG2_BUF_DBG("data", cred->credential, cred->clen);
}
	
//		6. A WRITE command is performed on the Reissue Code field.
{
	uint8_t data[1] = { 0 };
	uint16_t dlen = 0;
	uint8_t offset[3] = {0x0F, 0x00, 0x00};
	uint8_t length[3] = {1, 0x00, 0x00};
	LOG2_DBG("write data");
	ret = Reader_MFDF_WriteData(PHAL_MFDFEVX_COMMUNICATION_ENC, PHAL_MFDFEVX_APPLICATION_CHAINING, bFileNo, offset, data, length);
	if(ret){
		LOG2_ERR("Reader_MFDF_ReadData fail: %04X", ret);
		return 5;
	}
}

//		7. A COMMIT TRANSACTION command is performed to end exchange.

	uint8_t end_apdu[] = {0x90, 0xC7, 0x00, 0x00, 0x00};
	uint8_t *end_rapdu;
	uint16_t end_rlen;
	LOG2_DBG("COMMIT TRANSACTION");
	phpalI14443p4_Exchange(&gphNfcLib_Params.spalI14443p4, PH_EXCHANGE_DEFAULT, end_apdu, sizeof(end_apdu), &end_rapdu, &end_rlen);

//7. DESELECT credential.
	phpalI14443p4_Deselect(&gphNfcLib_Params.spalI14443p4);

	return 0;
}


//获取分散因子
static int reader_read_aw_div(key_set_t* key_set, uint8_t* div, uint8_t* dlen){
		int ret;
	//2. SELECT the Privacy application and authenticate using the undiversified key.
		uint32_t aid = key_set->aid;
		ret = Reader_MFDF_SelectApplication(PHAL_MFDFEVX_SELECT_PRIMARY_APP, (uint8_t*)&aid, NULL);
		if(ret){
			LOG2_ERR("select app(%06X) fail: %04X", aid, ret);
			return 1;
		}
	//3. Read the static UID/CSN of the credential following Privacy app authentication.
		uint8_t keyNo = 0;
		uint8_t key_no = 0;
		uint8_t ver = 0;
		void* key_store = phNfcLib_GetDataParams(PH_COMP_KEYSTORE);
		uint8_t *key_des = key_set->keys;
		phKeyStore_FormatKeyEntry(key_store, keyNo, key_set->key_type);
		phKeyStore_SetKeyAtPos(key_store, keyNo, 0, key_set->key_type, (uint8_t *)key_des, ver);
		ret = Reader_MFDF_Authenticate(PHAL_MFDFEVX_NO_DIVERSIFICATION, keyNo, ver, key_set->key_no, NULL, 0);
		if(ret){
			LOG2_ERR("auth fail: %04X", ret);
			return 2;
		}
		uint8_t uid[8];
		ret = Reader_MFDF_GetCardUID(PHAL_MFDFEVX_GET_CARD_UID_EXCHANGE_OPTION_OFF, 
			PHAL_MFDFEVX_GET_CARD_UID_OPTION_NUID_NOT_RETURNED, uid);
		if(ret){
			LOG2_ERR("Reader_MFDF_GetCardUID fail: %04X", ret);
			return 3;
		}
		LOG2_BUF_DBG("uid:", uid, 7);
		memcpy(div, uid, 7);
		*dlen = 7;
		return 0;
}

static int reader_read_aw_data(w_apple_wallet_param_t* param, uint8_t* div, uint8_t div_len, uint8_t* rdata, uint8_t* rlen){
	int ret;
	key_set_t* key_set = &param->key_set[1];
	void* key_store = phNfcLib_GetDataParams(PH_COMP_KEYSTORE);
//4. Diversify Credential application key using static UID from step 2.
//5. SELECT and authenticate Credential application using diversified key.
	uint32_t aid = key_set->aid;
	ret = Reader_MFDF_SelectApplication(PHAL_MFDFEVX_SELECT_PRIMARY_APP, (uint8_t*)&aid, NULL);
	if(ret){
		LOG2_ERR("Reader_MFDF_SelectApplication(%06X) fail: %04X", aid, ret);
		return 4;
	}
	uint8_t ver = 0;
	uint8_t keyNo = 1;
	uint8_t* key_aes = key_set->keys;
	phKeyStore_FormatKeyEntry(key_store, keyNo, key_set->key_type);
	phKeyStore_SetKeyAtPos(key_store, keyNo, 0,	 key_set->key_type, (uint8_t *)key_aes, ver);
	ret = Reader_MFDF_AuthenticateAES(PHAL_MFDFEVX_DIV_METHOD_CMAC, keyNo, ver, key_set->key_no, div, div_len);
	if(ret){
		LOG2_ERR("Reader_MFDF_Authenticate fail: %04X", ret);
		return 5;
	}
//6. READ first 40 bits of data from Credential application.
	uint8_t bFileNo = param->file_no;
	uint8_t *data;
	uint16_t dlen = 0;
	uint8_t offset[3] = {0x00, 0x00, 0x00};
	uint8_t length[3] = {param->file_size, 0x00, 0x00};
	ret = Reader_MFDF_ReadData(PHAL_MFDFEVX_COMMUNICATION_ENC, 0, bFileNo, offset, length, &data, &dlen);
	if(ret){
		LOG2_ERR("Reader_MFDF_ReadData fail: %04X", ret);
		return 6;
	}
	LOG2_BUF_DBG("data:", data, 16);

	memcpy(rdata, data, 16);
	*rlen = 16;
	
	uint8_t end_apdu[] = {0x90, 0xee, 0x00, 0x00, 0x00};
	uint8_t *end_rapdu;
	uint16_t end_rlen;
	phpalI14443p4_Exchange(&gphNfcLib_Params.spalI14443p4, PH_EXCHANGE_DEFAULT, end_apdu, sizeof(end_apdu), &end_rapdu, &end_rlen);
//7. DESELECT credential.
	phpalI14443p4_Deselect(&gphNfcLib_Params.spalI14443p4);

	return 0;
}

static uint32_t reader_read_apple_wallet(w_apple_wallet_param_t* param, credential_t* cred){
	int ret;
	uint8_t div[8];
	uint8_t div_len = 0;
	//获取分散因子
	ret = reader_read_aw_div(&param->key_set[0], div, &div_len);
	if(ret){
		return ret;
	}
	uint8_t rdata[32];
	uint8_t rlen = 0;
	//读数据
	ret = reader_read_aw_data(param, div, div_len, rdata, &rlen);
	if(ret){
		return ret;
	}
	uint8_t size = 5;
	memcpy(cred->credential, rdata, size);
	cred->clen = size;
	cred->nbit = size * 8;
	cred->tag.type = HFTAG_WAVELYNX;
	return 0;
}

//装载密钥
static void reader_wavelynx_loadkeys(void){
	key_t key;
	//装载苹果钱包密钥
	if(config_get_key(KID_APPLE_PRIV, &key) == 0){
		memcpy(apple_wallet_param.key_set[0].keys, key.data, 16);
	}
	
	if(config_get_key(KID_APPLE_CRED, &key) == 0){
		memcpy(apple_wallet_param.key_set[1].keys, key.data, 16);
	}

	//装载MF2GO密钥
	if(config_get_key(KID_GOOGLE, &key) == 0){
		memcpy(mf2go_param.keys, key.data, 16);
	}
}

int reader_read_wavelynx_tag(Reader_T* read, credential_t* cred){
	reader_wavelynx_loadkeys();
	int ret = reader_read_apple_wallet(&apple_wallet_param, cred);
	if(!ret){
		return ret;
	}
	if(ret > 2){
		return ret;
	}
	return reader_read_google_wallet(&mf2go_param, cred);
}

